package boun.swe.bob.dao;

import org.apache.log4j.Logger;
import org.hibernate.LockMode;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

/**
 * This dao is for common methods
 */
public abstract class GenericDaoImpl<E, PK extends Serializable> implements IGenericDao<E, PK> {

        private static final Logger LOG = Logger.getLogger(GenericDaoImpl.class);

        private Class<E> entityClass = null;
        protected HibernateTemplate hibernateTemplate = null;

    /**
     * This method is used for setting the data source
     * @param sf corresponds to SessionFactory
     */
        @Autowired
        public void setDataSource(SessionFactory sf) {
                hibernateTemplate = new HibernateTemplate(sf);
        }


    /**
     * This method saves the record
     *
     * @param newInstance the record to be saved
     * @return returns PK
     */
        @SuppressWarnings("unchecked") @Transactional
        public PK save(E newInstance) {
                return (PK) hibernateTemplate.save(newInstance);
        }

    /**
     * This method finds the record by specifying the primary key
     *
     * @param primaryKey is the primary key for the record
     * @return record
     */
        public E findByPrimaryKey(PK primaryKey) {
                return (E) hibernateTemplate.get(getEntityClass(), primaryKey);
        }

    /**
     * This method finds all records
     *
     * @return recordset
     */
        @SuppressWarnings("unchecked")
        public List<E> findAll() {
                return hibernateTemplate.findByCriteria(createDetachedCriteria());
        }

    /**
     * This method finds a record by a specified property
     *
     * @param propertyName name of the property
     * @param value value of the property
     * @return the record
     */
        @SuppressWarnings("unchecked")
        protected List<E> findAllByProperty(String propertyName, Object value) {
                DetachedCriteria criteria = createDetachedCriteria().add(Restrictions.eq(propertyName, value));
                return hibernateTemplate.findByCriteria(criteria);
        }

    /**
     * This method finds a record by a unique property
     *
     * @param propertyName name of the property
     * @param value value of the property
     * @return the record
     */
        protected E findByUniqueProperty(String propertyName,Object value) {
                List<E> all = findAllByProperty(propertyName, value);
                if (all.size() == 0) {
                        return null;
                } else {
                        return all.get(0);
                }
        }

    /**
     * This method finds a record by specified criteria
     *
     * @param criteria the criteria for the desired record
     * @return the record
     */
        @SuppressWarnings("unchecked")
        protected List<E> findByCriteria(DetachedCriteria criteria) {
                return hibernateTemplate.findByCriteria(criteria);
        }

    /**
     * This method returns the result of a query string
     *
     * @param queryString the query string
     * @param values the parameters of the query string
     * @return the record
     * @return the record
     */
        @SuppressWarnings("unchecked")
        protected List<E> find(String queryString, Object...values) {
                return hibernateTemplate.find(queryString, values);
        }

    /**
     * This method runs the bulk update query
     *
     * @param queryString the query string
     * @param values the parameters of the query string
     * @return result
     */
        public int bulkUpdate(String queryString, Object...values) {
                return hibernateTemplate.bulkUpdate(queryString, values);
        }

    /**
     * This method updates the data object
     *
     * @param transientObject object to be updated
     */
        @Transactional
        public void update(E transientObject) {
                hibernateTemplate.update(transientObject);
        }

    /**
     * This method saves the object if it does not exist in the table
     * or updates the record in the table if it exists
     *
     * @param transientObject object to be saved or updated
     */
        @Transactional
        public void saveOrUpdate(E transientObject) {
                hibernateTemplate.saveOrUpdate(transientObject);
        }

    /**
     * This method is for merging the object
     * 
     * @param transientObject object to be merged
     */
        @Transactional
        public void merge(E transientObject) {
                hibernateTemplate.merge(transientObject);
        }

    /**
     * This method is for deleting the record
     *
     * @param persistentObject object to be deleted
     */
        @Transactional()
        public void delete(E persistentObject) {
                if (persistentObject != null) {
                        hibernateTemplate.delete(persistentObject);
                } else {
                        LOG.warn(getClass().getSimpleName() + ": attempt to delete NULL object");
                }
        }

    /**
     * This method is for deleting a record by specifying a primary key
     *
     * @param primaryKey primary key
     */
        @Transactional
        public void deleteByPrimaryKey(PK primaryKey) {
                E entity = this.findByPrimaryKey(primaryKey);
                this.delete(entity);
        }

    /**
     * This method is for clearing all the records in the table
     */
        @Transactional
        public void deleteAll() {
                hibernateTemplate.bulkUpdate("DELETE FROM " + getEntityClass().getName());
        }

        /**
         * Locks an object for updating
         *
         * @param entity Entity
         */
        @Transactional
        public void lock(E entity) {
                hibernateTemplate.lock(entity, LockMode.PESSIMISTIC_WRITE);
        }

        @SuppressWarnings("unchecked")
        public final Class<E> getEntityClass() {
                if (this.entityClass == null) {
                        ParameterizedType pType = (ParameterizedType) getClass().getGenericSuperclass();
                        this.entityClass = (Class<E>) pType.getActualTypeArguments()[0];
                }
                return this.entityClass;
        }

        public DetachedCriteria createDetachedCriteria() {
                return DetachedCriteria.forClass(getEntityClass());
        }


}
